export class NesAudio {
    private AUDIO_BUFFERING = 512;
    private SAMPLE_COUNT = 4 * 1024;
    private SAMPLE_MASK = this.SAMPLE_COUNT - 1;
    private audio_samples_L = new Float32Array(this.SAMPLE_COUNT);
    private audio_samples_R = new Float32Array(this.SAMPLE_COUNT);
    private audio_read_cursor = 0;
    private audio_write_cursor = 0;

    constructor() {
        const audio_ctx = new AudioContext();
        const script_processor = audio_ctx.createScriptProcessor(this.AUDIO_BUFFERING, 0, 2);
        script_processor.onaudioprocess = (event: AudioProcessingEvent) => {
            this.audio_callback(event);
        };
        script_processor.connect(audio_ctx.destination);
    }

    audio_callback(event: AudioProcessingEvent) {
        const { audio_read_cursor, SAMPLE_MASK, audio_samples_L, audio_samples_R } = this;

        const dst = event.outputBuffer;
        const len = dst.length;

        let dst_l = dst.getChannelData(0);
        let dst_r = dst.getChannelData(1);
        for (let i = 0; i < len; i++) {
            const src_idx = (audio_read_cursor + i) & SAMPLE_MASK;
            dst_l[i] = audio_samples_L[src_idx];
            dst_r[i] = audio_samples_R[src_idx];
        }

        this.audio_read_cursor = (audio_read_cursor + len) & SAMPLE_MASK;
    }

    setAudioSample(l: number, r: number) {
        const { SAMPLE_MASK, audio_samples_L, audio_samples_R, audio_write_cursor } = this;
        audio_samples_L[audio_write_cursor] = l;
        audio_samples_R[audio_write_cursor] = r;
        this.audio_write_cursor = (audio_write_cursor + 1) & SAMPLE_MASK;
    }
}